package com.kk.demo.concurrent;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * AtomicStampedReference进行ABA的问题
 * 
 *  在运用CAS做Lock-Free操作中有一个经典的ABA问题：
 *  线程1准备用CAS将变量的值由A替换为B，
 *  在此之前，线程2将变量的值由A替换为C，又由C替换为A，
 *  然后线程1执行CAS时发现变量的值仍然为A，所以CAS成功。
 *  但实际上这时的现场已经和最初不同了，尽管CAS成功，但可能存在潜藏的问题
 *  
 *  专门为此问题而进行修改
 * 
* 源文件名：UserAtomicStampedReferenceDemo.java
* 文件版本：1.0.0
* 创建作者：liujun
* 创建日期：2016年11月11日
* 修改作者：liujun
* 修改日期：2016年11月11日
* 文件描述：TODO
* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.
*/
public class UserAtomicStampedReferenceDemo {

    /**
     * 摸拟用户帐户,现帐户上有100元钱,
     * 比如，一次充值，被两个线程执行，线程1先充值，被立即消费了，线程2来进行充值，
     * 发现还是原来的值，就又发生了一次充值，发生了重复充值的问题.导致了损失
    * @字段说明 AtomicLong
    */
    private static AtomicStampedReference<Long> atomicLong = new AtomicStampedReference<Long>(10l, 0);

    /**
     * 线程池对象
    * @字段说明 execut
    */
    private static final ExecutorService EXEC_RUN = Executors.newFixedThreadPool(10);

    /**
     * 充值
    * 方法描述
    * @param oldMoney
    * @创建日期 2016年11月11日
    */
    public void addMoney(AtomicStampedReference<Long> currAtomicTamp, AtomicStampedReference<Long> atomicLongInct) {
        long value = currAtomicTamp.getReference();

        if (atomicLong.compareAndSet(value, atomicLongInct.getReference() + value, currAtomicTamp.getStamp(),
                atomicLongInct.getStamp())) {
            System.out.println("充值成功，已经成功充值了，总数" + atomicLong.getReference() + "元");
        } else {
            System.out.println("当前时间戳:" + currAtomicTamp.getStamp() + "充值失败");
        }
    }

    public void userMoney(AtomicStampedReference<Long> atomicLongCorr, AtomicStampedReference<Long> atomicLongInctUse) {
        long value = atomicLongCorr.getReference();

        if (atomicLong.compareAndSet(value, atomicLongInctUse.getReference(), atomicLongCorr.getStamp(),
                atomicLongInctUse.getStamp())) {
            System.out.println("消费成功，已经数字为" + (atomicLong.getReference()) + "元");
        } else {
            System.out.println("当前时间戳:" + atomicLong.getStamp() + "消费失败");
        }
    }

    public static void main(String[] args) {

        UserAtomicStampedReferenceDemo atom = new UserAtomicStampedReferenceDemo();

        // 当前对象的初始值
        final AtomicStampedReference<Long> atomicLongCurrValue = new AtomicStampedReference<Long>(10l, 0);
        // 递增
        final AtomicStampedReference<Long> atomicLongInctAdd = new AtomicStampedReference<Long>(20l, 2);
        // 递减
        final AtomicStampedReference<Long> atomicLongInctUse = new AtomicStampedReference<Long>(20l, 3);

        // 进行充值
        EXEC_RUN.submit(() -> {
            // 首先充值10元
            atom.addMoney(atomicLongCurrValue, atomicLongInctAdd);

            AtomicStampedReference<Long> atomicLongSum = new AtomicStampedReference<Long>(30l, 2);

            // 进行消费
            atom.userMoney(atomicLongSum, atomicLongInctUse);
        });

        // 进行消费
        EXEC_RUN.submit(() -> {
            try {
                Thread.currentThread().sleep(1000l);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 由于时间戳还是之前的所以，充值会失败
            atom.addMoney(atomicLongCurrValue, atomicLongInctAdd);

            System.out.println("完成最终的充值为失败:" + atomicLong + ",value:" + atomicLong.getReference() + ",temp:"
                    + atomicLong.getStamp());
        });
    }

}
